package org.yaac.server.egql.evaluator;

import static com.google.common.collect.Sets.newHashSet;

import java.util.HashSet;
import java.util.Set;

import org.yaac.server.egql.EntityProperty;
import org.yaac.server.egql.exception.EGQLException;
import org.yaac.server.egql.processor.ProcessData.ProcessDataRecord;
import org.yaac.server.util.DatastoreUtil;



/**
 * @author Max Zhu (thebbsky@gmail.com)
 *
 */
public class RelationEvaluator extends Evaluator {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public enum Type {
		EQUALS,
		NOT_EQUALS,
		GREATER_THAN, 
		GREATER_THAN_OR_EQUALS,
		LESS_THAN, 
		LESS_THAN_OR_EQUALS, 
		LIKE, 
		NOT_LIKE,
	}
	
	private final Evaluator op1;
	
	private final Evaluator op2;

	private final Type type;
	
	/**
	 * @param op1
	 * @param type
	 * @param op2
	 */
	public RelationEvaluator(Evaluator op1, Type type, Evaluator op2) {
		this.op1 = op1;
		this.type = type;
		this.op2 = op2;
	}
	
	@Override
	public EvaluationResult evaluate(ProcessDataRecord record) {
		EvaluationResult r1 = op1.evaluate(record);
		EvaluationResult r2 = op2.evaluate(record);
		
		return new EvaluationResult(
				evaluateBool(r1.getPayload(), r2.getPayload(), this.type), r1, r2).withTitle(this.getText());
	}

	private static Boolean evaluateBool(Object arg0, Object arg1, Type type) {
		switch(type) {
		case EQUALS:
			return DatastoreUtil.deterministicCompare(arg0, arg1) == 0;
		case GREATER_THAN_OR_EQUALS:
			return DatastoreUtil.deterministicCompare(arg0, arg1) >= 0;
		case LESS_THAN_OR_EQUALS:
			return DatastoreUtil.deterministicCompare(arg0, arg1) <= 0;
		case GREATER_THAN:
			return DatastoreUtil.deterministicCompare(arg0, arg1) > 0;
		case LESS_THAN:
			return DatastoreUtil.deterministicCompare(arg0, arg1) < 0;
		case NOT_EQUALS:
			return DatastoreUtil.deterministicCompare(arg0, arg1) != 0;
		case LIKE:
			if (arg0 instanceof String && arg1 instanceof String) {
				String input = (String) arg0;
				String pattern = (String) arg1;
				
				if (pattern.startsWith("%")) {
					pattern = pattern.substring(1);	//remove the first char 
					
					if (pattern.endsWith("%")) {
						pattern = pattern.substring(0, pattern.length() - 1);	// remove the last char
						return input.contains(pattern);
					} else {
						return input.endsWith(pattern);
					}
				} else if (pattern.endsWith("%")) {
					pattern = pattern.substring(0, pattern.length() - 2);	// remove the last char
					return input.startsWith(pattern);
				} else {	// pattern doesn't contain %
					return input.equals(pattern);
				}
			} else {
				return false;	// any of them not a string, not like
			}
		case NOT_LIKE:
			return !evaluateBool(arg0, arg1, Type.LIKE);
		default:
			throw new UnsupportedOperationException("unsupported type " + type);
		} 
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = super.hashCode();
		result = prime * result + ((op1 == null) ? 0 : op1.hashCode());
		result = prime * result + ((op2 == null) ? 0 : op2.hashCode());
		result = prime * result + ((type == null) ? 0 : type.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!super.equals(obj))
			return false;
		if (getClass() != obj.getClass())
			return false;
		RelationEvaluator other = (RelationEvaluator) obj;
		if (op1 == null) {
			if (other.op1 != null)
				return false;
		} else if (!op1.equals(other.op1))
			return false;
		if (op2 == null) {
			if (other.op2 != null)
				return false;
		} else if (!op2.equals(other.op2))
			return false;
		if (type != other.type)
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "RelationEvaluator [op1=" + op1 + ", op2=" + op2 + ", type=" + type + "]";
	}

	@Override
	public Set<AggregationEvaluator> aggregationChildren() {
		Set<AggregationEvaluator> result = newHashSet();
		result.addAll(this.op1.aggregationChildren());
		result.addAll(this.op2.aggregationChildren());
		return result;
	}
	
	@Override
	public Set<EntityProperty> nonAggregationProperties() {
		Set<EntityProperty> result = newHashSet();
		result.addAll(this.op1.nonAggregationProperties());
		result.addAll(this.op2.nonAggregationProperties());
		return result;
	}

	@Override
	public void validate() throws EGQLException {
		
	}
}
